package cosmos.example;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import au.com.bytecode.opencsv.CSVReader;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import cosmos.Cosmos;
import cosmos.options.Defaults;
import cosmos.records.impl.MultimapRecord;
import cosmos.records.values.RecordValue;
import cosmos.results.Column;
import cosmos.store.Store;
public class LoadBuildingPermits implements Runnable {
private static final Logger log = LoggerFactory.getLogger(LoadBuildingPermits.class);
public static final Column ID = Column.create("ID");
public static final Long MAX_RESULTS = Long.MAX_VALUE;
protected Cosmos cosmos;
protected Store id;
protected File csvData;
protected Long maxResultsToLoad;
public LoadBuildingPermits(Cosmos cosmos, Store id, File csvData) {
checkNotNull(cosmos);
checkNotNull(id);
checkNotNull(csvData);
checkArgument(csvData.exists() && csvData.isFile() && csvData.canRead(),
csvData.getAbsoluteFile() + " is not a readable file");
this.cosmos = cosmos;
this.id = id;
this.csvData = csvData;
// Control how many results from the data set are loaded
this.maxResultsToLoad = MAX_RESULTS;
// this.maxResultsToLoad = 50000l;
}
@Override
public void run() {
ArrayList<String> schema;
// Open up a regular reader to get the header line out of the file,
// and construct a schema for this csv file.
try {
BufferedReader bufReader = new BufferedReader(new FileReader(csvData));
String schemaLine = bufReader.readLine();
String[] schemaEntries = StringUtils.split(schemaLine, Defaults.COMMA);
schema = Lists.newArrayListWithCapacity(schemaEntries.length);
for (String schemaEntry : schemaEntries) {
schema.add(StringUtils.trim(schemaEntry));
}
bufReader.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
CSVReader reader = null;
try {
reader = new CSVReader(new FileReader(csvData));
// Consume the schema line as we don't want to treat it as a record
reader.readNext();
String[] data;
long lineNumber = 0l;
long resultsInserted = 0l;
int numFlushes = 0;
int logUpdateEveryNBuffers = 5;
int itemsToBuffer = 10000;
long entriesLoadedSinceLastLog = 0l;
// Buffer 100 results to ammortize the BatchWriter costs underneath
ArrayList<MultimapRecord> cachedResults = Lists.newArrayListWithCapacity(itemsToBuffer + 1);
while (null != (data = reader.readNext())) {
lineNumber++;
// Make sure our line of data has the expected number of columns
if (data.length != schema.size()) {
log.error("Data on line {} did not match expected column size: {}", lineNumber, data);
continue;
}
// Make the Multimap of column to svalue
HashMultimap<Column,RecordValue<?>> metadata = HashMultimap.create();
for (int i = 0; i < schema.size(); i++) {
String name = schema.get(i);
String value = StringUtils.trim(data[i]);
if (!StringUtils.isBlank(value)) {
metadata.put(Column.create(name), RecordValue.create(value, Defaults.EMPTY_VIS));
}
}
// Make sure we can extract a column as the docID
if (!metadata.containsKey(ID) || 1 != metadata.get(ID).size()) {
log.error("Expected to find one {} column in record: {}", ID, metadata.toString());
}
RecordValue<?> docId = metadata.get(ID).iterator().next();
// Add the record to our buffer
cachedResults.add(new MultimapRecord(metadata, docId.value().toString(), Defaults.EMPTY_VIS));
// Flush the buffer when it gets big enough
if (itemsToBuffer < cachedResults.size()) {
try {
cosmos.addResults(this.id, cachedResults);
numFlushes++;
entriesLoadedSinceLastLog += cachedResults.size();
} catch (Exception e) {
log.error("Problem adding results to cosmos", e);
throw new RuntimeException(e);
}
if (0 == numFlushes % logUpdateEveryNBuffers) {
log.info("Loaded {} records", entriesLoadedSinceLastLog);
numFlushes = 0;
entriesLoadedSinceLastLog = 0l;
}
resultsInserted += cachedResults.size();
cachedResults.clear();
}
// Observe a limit on how many results we'll load after each batch
if (resultsInserted >= this.maxResultsToLoad) {
break;
}
}
if (0 < numFlushes) {
log.info("Loaded {} records", entriesLoadedSinceLastLog);
}
// Make sure we catch the tail-end of any results we buffered
if (!cachedResults.isEmpty()) {
try {
cosmos.addResults(this.id, cachedResults);
log.info("Loaded {} records", cachedResults.size());
// Try to force a cleanup on next GC
cachedResults = null;
} catch (Exception e) {
log.error("Problem adding results to cosmos", e);
throw new RuntimeException(e);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}